package com.senyint.hip.fhir.provider;

import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.*;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * *******description*******
 * 就诊
 * *************************
 *
 * @author ChenPan
 * @date 2018/10/29 10:49
 */
@Component
public class EncounterProvider implements IResourceProvider {

    /**
     * 临时存储数据
     */
    private Map<Long, Deque<Encounter>> encounterRes = new HashMap<Long, Deque<Encounter>>();

    private long myNextId = 1;

    @Override
    public Class<Encounter> getResourceType() {
        return Encounter.class;
    }

    @Search
    public List<Encounter> findDrUsingArbitraryCtriteria() {
        LinkedList<Encounter> retVal = new LinkedList<Encounter>();

        for (Deque<Encounter> nextEncList : encounterRes.values()) {
            Encounter nextEnc = nextEncList.getLast();
            retVal.add(nextEnc);
        }

        return retVal;
    }

    @Create()
    public MethodOutcome createEncounter(@ResourceParam Encounter encounter) {

        long id = myNextId++;

        String versionId = addNewVersionEnc(encounter, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("Encounter", String.valueOf(id), versionId));

        // You can also add an OperationOutcome resource to return
        // This part is optional though:
        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("Encounter", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("新增就诊成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }

    private String addNewVersionEnc(Encounter enc, long id) {

        Date publishedDate;

        if (!encounterRes.containsKey(id)) {
            encounterRes.put(id, new LinkedList<Encounter>());
            publishedDate = InstantDt.withCurrentTime().getValue();
        } else {
            Encounter oldEnc = encounterRes.get(id).getLast();
            publishedDate = oldEnc.getMeta().getLastUpdated();

        }


        enc.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
        Deque<Encounter> existingVersions = encounterRes.get(id);

        String newVersion = Integer.toString(existingVersions.size());

        //版本号
        enc.getMeta().setVersionId(newVersion);

        // Create an ID with the new version and assign it back to the resource
        IdDt newId = new IdDt("Encounter", Long.toString(id), newVersion);
        enc.setId(newId);

        existingVersions.add(enc);
        return newVersion;
    }

    @Search()
    public List<Encounter> findByPatientName(@RequiredParam(name = "name") String patientName) {

        LinkedList<Encounter> retVal = new LinkedList<Encounter>();

      
        for (Deque<Encounter> nextEnclist : encounterRes.values()) {
            Encounter nextEnc = nextEnclist.getLast();

            if (patientName.equals(nextEnc.getSubject().getDisplay())) {
                retVal.add(nextEnc);
                
            }

        }

        return retVal;
    }

    
    @Search()
    public List<Encounter> findByPatientNameAndId(@RequiredParam(name = "name") String patientName,@RequiredParam(name ="_id") String id) {

        LinkedList<Encounter> retVal = new LinkedList<Encounter>();

        NAMELOOP:
        for (Deque<Encounter> nextEnclist : encounterRes.values()) {
            Encounter nextEnc = nextEnclist.getLast();
             Long idPartAsLong = nextEnc.getIdElement().getIdPartAsLong();
             
             if(StringUtils.isNotEmpty(patientName)&&StringUtils.isNotEmpty(id)) {
            	 if (patientName.equals(nextEnc.getSubject().getDisplay())&& id.equals(idPartAsLong.toString())) {
                     retVal.add(nextEnc);
                     break NAMELOOP;
                 }
             }else if (StringUtils.isNotEmpty(patientName)&&StringUtils.isEmpty(id)) {
            	 
            	 if (patientName.equals(nextEnc.getSubject().getDisplay())) {
                     retVal.add(nextEnc);
                     break NAMELOOP;
                 }
            	 
             }else if (StringUtils.isEmpty(patientName)&&StringUtils.isEmpty(id)) {
            	 if (id.equals(idPartAsLong.toString())) {
                     retVal.add(nextEnc);
                     break NAMELOOP;
                 }
             }
           

        }

        return retVal;
    }
    
    @Search()
    public List<Encounter> findById(@RequiredParam(name ="_id") String id) {

        LinkedList<Encounter> retVal = new LinkedList<Encounter>();

        NAMELOOP:
        for (Deque<Encounter> nextEnclist : encounterRes.values()) {
            Encounter nextEnc = nextEnclist.getLast();
             Long idPartAsLong = nextEnc.getIdElement().getIdPartAsLong();
             
             if (StringUtils.isNotEmpty(id)) {
            	 if (id.equals(idPartAsLong.toString())) {
                     retVal.add(nextEnc);
                     break NAMELOOP;
                 }
             }
           

        }

        return retVal;
    }
    @Read(version = true)
    public Encounter readEncounter(@IdParam IdType theId) {
        Deque<Encounter> retVal;
        try {
            retVal = encounterRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        if (theId.hasVersionIdPart() == false) {
            return retVal.getLast();
        } else {
            for (Encounter nextVersion : retVal) {
                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
                if (theId.getVersionIdPart().equals(nextVersionId)) {
                    return nextVersion;
                }
            }
            // No matching version
            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
        }

    }

    @History()
    public List<Encounter> readEncounterBundle(@IdParam IdType theId) {
        Deque<Encounter> retVal;
        try {
            retVal = encounterRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        List<Encounter> encList = new ArrayList<>();
        for (Encounter enc : retVal) {
            encList.add(enc);
        }

        return encList;
    }

    @Update()
    public MethodOutcome updateEncounter(@IdParam IdType theId, @ResourceParam Encounter enc) {

        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!encounterRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        String versionId = addNewVersionEnc(enc, id);

        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("Encounter", String.valueOf(id), versionId));

        // You can also add an OperationOutcome resource to return
        // This part is optional though:
        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("Encounter", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("更新就诊成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }


    @Delete()
    public MethodOutcome removeEncounter(@IdParam IdType theId) {
        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        if (!encounterRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        encounterRes.remove(id);

        MethodOutcome retVal = new MethodOutcome();
        OperationOutcome outcome = new OperationOutcome();
        outcome.addIssue().setDiagnostics("删除成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }


}
